////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxbpsgic30.cpp
/// @brief BPSGIC30 class implementation
///        Corrects localized Gb/Gr imbalance artifacts in image
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "bps_data.h"
#include "camxbpsgic30.h"
#include "camxiqinterface.h"
#include "camxnode.h"
#include "camxtuningdatamanager.h"
#include "parametertuningtypes.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::Create(
    BPSModuleCreateData* pCreateData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pCreateData)
    {
        pCreateData->pModule = CAMX_NEW BPSGIC30;

        if (NULL != pCreateData->pModule)
        {
            result = pCreateData->pModule->Initialize(&pCreateData->initializationData);
            if (CamxResultSuccess != result)
            {
                CAMX_LOG_ERROR(CamxLogGroupISP, "Module initialization failed !!");
                BPSGIC30* pModule = reinterpret_cast<BPSGIC30*>(pCreateData->pModule);
                pModule->Destroy();
                pCreateData->pModule = NULL;
            }
        }
        else
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT_ALWAYS_MESSAGE("Memory allocation failed");
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupISP, "Null input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::Initialize(
    const ISPInputData* pInputData)
{
    ResourceParams cmdBufferConfig = { 0 };
    CamxResult     result          = CamxResultSuccess;

    // Initialize GIC Noise DMI tables for Firmware BPS IQ Settings payload
    m_pLUTCmdBufferManager = CAMX_NEW CmdBufferManager();
    if (NULL != m_pLUTCmdBufferManager)
    {
        cmdBufferConfig.resourceSize                 = BPSGICNoiseLUTBufferSize;
        cmdBufferConfig.poolSize                     = (pInputData->requestQueueDepth * cmdBufferConfig.resourceSize);
        cmdBufferConfig.usageFlags.cmdBuffer         = 1;
        cmdBufferConfig.alignment                    = CamxCommandBufferAlignmentInBytes;
        cmdBufferConfig.cmdParams.enableAddrPatching = 0;
        cmdBufferConfig.cmdParams.maxNumNestedAddrs  = 0;
        cmdBufferConfig.memFlags                     = CSLMemFlagUMDAccess;
        cmdBufferConfig.cmdParams.type               = CmdType::CDMDMI;
        cmdBufferConfig.pDeviceIndices               = pInputData->pipelineBPSData.pDeviceIndex;
        cmdBufferConfig.numDevices                   = 1;

        result = m_pLUTCmdBufferManager->Initialize(&cmdBufferConfig);
        if (result != CamxResultSuccess)
        {
            CAMX_LOG_ERROR(CamxLogGroupPProc, "Unable to initilizee LUTCmdBufferManager, no memory");
        }
    }
    else
    {
        result = CamxResultENoMemory;
        CAMX_ASSERT_ALWAYS_MESSAGE("Memory allocation failed");
    }

    result = AllocateCommonLibraryData();
    if (result != CamxResultSuccess)
    {
        CAMX_LOG_ERROR(CamxLogGroupPProc, "Unable to initilizee common library data, no memory");
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::AllocateCommonLibraryData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::AllocateCommonLibraryData()
{
    CamxResult result = CamxResultSuccess;

    UINT interpolationSize = (sizeof(gic_3_0_0::gic30_rgn_dataType) * (GIC30MaxmiumNonLeafNode + 1));

    if (NULL == m_dependenceData.pInterpolationData)
    {
        // Allocate memory for gic_3_0_0::gic30_rgn_dataType
        m_dependenceData.pInterpolationData = CAMX_CALLOC(interpolationSize);
        if (NULL == m_dependenceData.pInterpolationData)
        {
            result = CamxResultENoMemory;
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::CheckDependenceChange
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL BPSGIC30::CheckDependenceChange(
    ISPInputData* pInputData)
{
    BOOL isChanged = FALSE;

    if ((NULL != pInputData)           &&
        (NULL != pInputData->pHwContext))
    {
        if (NULL != pInputData->pOEMIQSetting)
        {
            m_moduleEnable = (static_cast<OEMBPSIQSetting*>(pInputData->pOEMIQSetting))->GICEnable;

            isChanged      = (TRUE == m_moduleEnable);
            if (TRUE == isChanged)
            {
                IQInterface::s_interpolationTable.BPSGIC30TriggerUpdate(&(pInputData->triggerData), &m_dependenceData);
            }
        }
        else
        {
            TuningDataManager* pTuningManager = pInputData->pTuningDataManager;
            CAMX_ASSERT(NULL != pTuningManager);

            // Search through the tuning data (tree), only when there
            // are changes to the tuning mode data as an optimization
            if ((TRUE == pInputData->tuningModeChanged)    &&
                (TRUE == pTuningManager->IsValidChromatix()))
            {
                CAMX_ASSERT(NULL != pInputData->pTuningData);

                m_pChromatix = pTuningManager->GetChromatix()->GetModule_gic30_bps(
                    reinterpret_cast<TuningMode*>(&pInputData->pTuningData->TuningMode[0]),
                    pInputData->pTuningData->noOfSelectionParameter);
            }

            CAMX_ASSERT(NULL != m_pChromatix);
            if (NULL != m_pChromatix)
            {
                if (m_pChromatix != m_dependenceData.pChromatix)
                {
                    m_dependenceData.pChromatix = m_pChromatix;
                    m_moduleEnable              = m_pChromatix->enable_section.gic_global_enable;

                    isChanged                   = (TRUE == m_moduleEnable);
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupISP, "Failed to get Chromatix");
            }
        }
        if (TRUE == m_moduleEnable)
        {
            if (TRUE ==
                IQInterface::s_interpolationTable.
                BPSGIC30TriggerUpdate(&(pInputData->triggerData), &m_dependenceData))
            {
                isChanged = TRUE;
            }
        }
    }
    else
    {
        if (NULL != pInputData)
        {
            CAMX_LOG_ERROR(CamxLogGroupISP,
                           "Invalid Input: pHwContext %p",
                           pInputData->pHwContext);
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupISP, "Null Input Pointer");
        }
    }

    return isChanged;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::WriteLUTtoDMI
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::WriteLUTtoDMI(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    // CDM pack the DMI buffer and patch the Red and Blue LUT DMI buffer into CDM pack
    if ((NULL != pInputData) && (NULL != pInputData->pDMICmdBuffer))
    {
        result = PacketBuilder::WriteDMI(pInputData->pDMICmdBuffer,
                                         regBPS_BPS_0_CLC_GIC_DMI_CFG,
                                         GICNoiseLUT,
                                         m_pLUTDMICmdBuffer,
                                         0,
                                         BPSGICNoiseLUTBufferSize);

        if (CamxResultSuccess == result)
        {
            ///< Switch LUT Bank select immediately after writing
            m_dependenceData.LUTBankSel = (m_dependenceData.LUTBankSel == GICLUTBank0) ? GICLUTBank1 : GICLUTBank0;
        }
        else
        {
            CAMX_ASSERT_ALWAYS_MESSAGE("Failed to write DMI data");
        }
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::CreateCmdList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::CreateCmdList(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    result = WriteLUTtoDMI(pInputData);

    if ((CamxResultSuccess == result) && (NULL != pInputData->pCmdBuffer))
    {
        CmdBuffer* pCmdBuffer = pInputData->pCmdBuffer;

        result = PacketBuilder::WriteRegRange(pCmdBuffer,
                                              regBPS_BPS_0_CLC_GIC_GIC_FILTER_CFG,
                                              (sizeof(BPSGIC30RegConfig) / RegisterWidthInBytes),
                                              reinterpret_cast<UINT32*>(&m_regCmd));

        if (CamxResultSuccess != result)
        {
            CAMX_ASSERT_ALWAYS_MESSAGE("Failed to write command buffer");
        }
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_ASSERT_ALWAYS_MESSAGE("Invalid Input pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::FetchDMIBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::FetchDMIBuffer()
{
    CamxResult      result          = CamxResultSuccess;
    PacketResource* pPacketResource = NULL;

    CAMX_ASSERT(NULL != m_pLUTCmdBufferManager);

    // Recycle the last updated LUT DMI cmd buffer
    if (NULL != m_pLUTDMICmdBuffer)
    {
        m_pLUTCmdBufferManager->Recycle(m_pLUTDMICmdBuffer);
    }

    // fetch a fresh LUT DMI cmd buffer
    result = m_pLUTCmdBufferManager->GetBuffer(&pPacketResource);
    if (CamxResultSuccess == result)
    {
        m_pLUTDMICmdBuffer = static_cast<CmdBuffer*>(pPacketResource);
    }
    else
    {
        m_pLUTDMICmdBuffer = NULL;
        result             = CamxResultEUnableToLoad;
        CAMX_ASSERT_ALWAYS_MESSAGE("Failed to fetch DMI Buffer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::RunCalculation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::RunCalculation(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pInputData)
    {
        result = FetchDMIBuffer();

        if (CamxResultSuccess == result)
        {
            GIC30OutputData outputData;

            outputData.pDMIData = reinterpret_cast<UINT32*>(m_pLUTDMICmdBuffer->BeginCommands(BPSGIC30DMILengthDword));
            CAMX_ASSERT(NULL != outputData.pDMIData);

            outputData.pRegCmd       = &m_regCmd;
            outputData.pModuleConfig = &m_moduleConfig;

            result = IQInterface::BPSGIC30CalculateSetting(&m_dependenceData, pInputData->pOEMIQSetting, &outputData);
            if (CamxResultSuccess == result)
            {
                m_pLUTDMICmdBuffer->CommitCommands();
                if (NULL != pInputData->pBPSTuningMetadata)
                {
                    m_pGICNoiseLUT = outputData.pDMIData;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupISP, "GIC Calculation Failed. result %d", result);
            }
        }
        /// @todo (CAMX-666) replace #if DEBUG with set properties
#if DEBUG
        DumpRegConfig();
#endif // DEBUG
    }
    else
    {
        result = CamxResultEInvalidPointer;
        CAMX_ASSERT_ALWAYS_MESSAGE("Null Input Pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::ValidateDependenceParams
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::ValidateDependenceParams(
    const ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pInputData)
    {

        if ((NULL == pInputData->pHwContext) || (NULL == pInputData->pAECUpdateData))
        {
            CAMX_LOG_ERROR(CamxLogGroupISP,
                "Invalid Input: pInputData %p pNewAECUpdate %p  HwContext %p",
                pInputData,
                pInputData->pAECUpdateData,
                pInputData->pHwContext);
            result = CamxResultEFailed;
        }
    }
    else
    {
        result = CamxResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupISP, "Null Input Pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::DumpRegConfig
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID BPSGIC30::DumpRegConfig() const
{
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC Filter config                  [0x%x]", m_regCmd.filterConfig);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC thin line noise offset         [0x%x]", m_regCmd.lineNoiseOffset);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC anchor and base setting 0      [0x%x]", m_regCmd.anchorBaseSetting0);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC anchor and base setting 1      [0x%x]", m_regCmd.anchorBaseSetting1);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC anchor and base setting 2      [0x%x]", m_regCmd.anchorBaseSetting2);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC anchor and base setting 3      [0x%x]", m_regCmd.anchorBaseSetting3);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC anchor and base setting 4      [0x%x]", m_regCmd.anchorBaseSetting4);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC anchor and base setting 5      [0x%x]", m_regCmd.anchorBaseSetting5);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC slope shift setting 0          [0x%x]", m_regCmd.slopeShiftSetting0);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC slope shift setting 1          [0x%x]", m_regCmd.slopeShiftSetting1);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC slope shift setting 2          [0x%x]", m_regCmd.slopeShiftSetting2);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC slope shift setting 3          [0x%x]", m_regCmd.slopeShiftSetting3);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC slope shift setting 4          [0x%x]", m_regCmd.slopeShiftSetting4);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC slope shift setting 5          [0x%x]", m_regCmd.slopeShiftSetting5);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC horizontal and vertical offset [0x%x]", m_regCmd.horizontalVerticalOffset);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC R square init                  [0x%x]", m_regCmd.squareInit);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC scale shift                    [0x%x]", m_regCmd.scaleShift);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC PNR noise scale 0              [0x%x]", m_regCmd.noiseRatioScale0);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC PNR noise scale 1              [0x%x]", m_regCmd.noiseRatioScale1);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC PNR noise scale 2              [0x%x]", m_regCmd.noiseRatioScale2);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC PNR noise scale 3              [0x%x]", m_regCmd.noiseRatioScale3);
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "GIC noise filter strength          [0x%x]", m_regCmd.noiseRatioFilterStrength);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::UpdateBPSInternalData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID BPSGIC30::UpdateBPSInternalData(
    const ISPInputData* pInputData
    ) const
{
    BpsIQSettings*  pBPSIQSettings = reinterpret_cast<BpsIQSettings*>(pInputData->pipelineBPSData.pIQSettings);
    CAMX_ASSERT(NULL != pBPSIQSettings);

    pBPSIQSettings->gicParameters.moduleCfg.EN     = m_moduleConfig.moduleConfig.bitfields.EN;
    pBPSIQSettings->gicParameters.moduleCfg.PNR_EN = m_moduleConfig.moduleConfig.bitfields.PNR_EN;
    pBPSIQSettings->gicParameters.moduleCfg.GIC_EN = m_moduleConfig.moduleConfig.bitfields.GIC_EN;

    // Post tuning metadata if setting is enabled
    if (NULL != pInputData->pBPSTuningMetadata)
    {
        CAMX_STATIC_ASSERT(BPSGICNoiseLUTBufferSize <=
                          sizeof(pInputData->pBPSTuningMetadata->BPSDMIData.packedLUT.GICLUT));
        CAMX_STATIC_ASSERT(sizeof(BPSGIC30RegConfig) <= sizeof(pInputData->pBPSTuningMetadata->BPSGICData));

        if (NULL != m_pGICNoiseLUT)
        {
            Utils::Memcpy(&pInputData->pBPSTuningMetadata->BPSDMIData.packedLUT.GICLUT,
                          m_pGICNoiseLUT,
                          BPSGICNoiseLUTBufferSize);
        }
        Utils::Memcpy(&pInputData->pBPSTuningMetadata->BPSGICData,
                      &m_regCmd,
                      sizeof(BPSGIC30RegConfig));
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::Execute
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult BPSGIC30::Execute(
    ISPInputData* pInputData)
{
    CamxResult result = CamxResultSuccess;

    if (NULL != pInputData)
    {
        // Check if dependent is valid and been updated compared to last request
        result = ValidateDependenceParams(pInputData);

        if ((CamxResultSuccess == result) && (TRUE == m_moduleEnable))
        {
            if (TRUE == CheckDependenceChange(pInputData))
            {
                result = RunCalculation(pInputData);
            }

            if ((CamxResultSuccess == result) && (TRUE == m_moduleEnable))
            {
                result = CreateCmdList(pInputData);
            }

            if (CamxResultSuccess == result)
            {
                UpdateBPSInternalData(pInputData);
            }
            else
            {
                CAMX_ASSERT_ALWAYS_MESSAGE("Operation failed %d", result);
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupISP, "Parameter dependencies failed");
        }
    }
    else
    {
        result = CamxResultEInvalidArg;
        CAMX_ASSERT_ALWAYS_MESSAGE("Null Input Pointer");
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::DeallocateCommonLibraryData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID BPSGIC30::DeallocateCommonLibraryData()
{
    if (NULL != m_dependenceData.pInterpolationData)
    {
        CAMX_FREE(m_dependenceData.pInterpolationData);
        m_dependenceData.pInterpolationData = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::BPSGIC30
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BPSGIC30::BPSGIC30()
{
    m_type         = ISPIQModuleType::BPSGIC;
    m_moduleEnable = TRUE;
    m_numLUT       = MaxGICNoiseLUT;
    m_cmdLength    = PacketBuilder::RequiredWriteRegRangeSizeInDwords(sizeof(BPSGIC30RegConfig) / RegisterWidthInBytes);

    m_pGICNoiseLUT = NULL;
    m_pChromatix   = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BPSGIC30::~BPSGIC30
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BPSGIC30::~BPSGIC30()
{
    DeallocateCommonLibraryData();
    if (NULL != m_pLUTCmdBufferManager)
    {
        if (NULL != m_pLUTDMICmdBuffer)
        {
            m_pLUTCmdBufferManager->Recycle(m_pLUTDMICmdBuffer);
            m_pLUTDMICmdBuffer = NULL;
        }

        m_pLUTCmdBufferManager->Uninitialize();
        CAMX_DELETE m_pLUTCmdBufferManager;
        m_pLUTCmdBufferManager = NULL;
    }

    m_pChromatix = NULL;
}

CAMX_NAMESPACE_END
